#if defined(_MSC_VER)
#include "SDL.h"
#else
#include "SDL/SDL.h"
#endif

#include "ThreeDCubeGame.h"
#include "ThreeDCubePlayer.h"

/** Constructor */
ThreeDCubePlayer::ThreeDCubePlayer(BaseGameEngine* pGame, int iMapX, int iMapY)
: DisplayableObject( pGame )
, m_iCurrentMapX(iMapX)
, m_iCurrentMapY(iMapY)
, m_iNextMapX(iMapX)
, m_iNextMapY(iMapY)
, m_iCurrentMoveDirection(0)
{
	m_iStartDrawPosX = 0;
	m_iStartDrawPosY = 0;
	m_iDrawWidth = ThreeDCubeGame::BLOCK_SIZE*2;
	m_iDrawHeight = ThreeDCubeGame::BLOCK_SIZE*2;
	m_iCurrentScreenX = ThreeDCubeGame::MAP_X_OFFSET+ThreeDCubeGame::BLOCK_SIZE*iMapX;
	m_iCurrentScreenY = ThreeDCubeGame::MAP_Y_OFFSET+ThreeDCubeGame::BLOCK_SIZE*2*iMapY - 15;
	m_iPreviousScreenX = m_iCurrentScreenX;
	m_iPreviousScreenY = m_iCurrentScreenY;
	SetMovementPosition( 0 );
}

/** Destructor */
ThreeDCubePlayer::~ThreeDCubePlayer(void)
{
}

/**
Draw the player object.
*/
void ThreeDCubePlayer::Draw()
{
	ThreeDCubeGame* pGame= dynamic_cast<ThreeDCubeGame*> (m_pGame);
	// concentric circles for pseudo-sphere
	int iRadiusSquared = (m_iDrawWidth/3) * (m_iDrawWidth/3);
	int iCentreX = m_iCurrentScreenX + m_iStartDrawPosX + m_iDrawWidth/2;
	int iCentreY = m_iCurrentScreenY + m_iStartDrawPosY + m_iDrawHeight/2;
	for ( int iX = m_iCurrentScreenX + m_iStartDrawPosX ; iX < (m_iCurrentScreenX + m_iStartDrawPosX + m_iDrawWidth) ; iX++ )
		for ( int iY = m_iCurrentScreenY + m_iStartDrawPosY ; iY < (m_iCurrentScreenY + m_iStartDrawPosY + m_iDrawHeight) ; iY++ )
			if ( ( (iX-iCentreX)*(iX-iCentreX) + (iY-iCentreY)*(iY-iCentreY) ) <= iRadiusSquared )
			{
				unsigned int uiColour = (0xB0 * ((iX-iCentreX)*(iX-iCentreX) + (iY-iCentreY)*(iY-iCentreY))) / iRadiusSquared;
				uiColour = 0xff - uiColour;
				GetGame()->SetScreenPixel( iX, iY, 0x010001 * uiColour );
			}
	StoreLastScreenPositionAndUpdateRect();
}

/**
Handle the update action, moving the object and/or handling any game logic
*/
void ThreeDCubePlayer::DoUpdate( int iCurrentTime )
{
	ThreeDCubeGame* pGame= dynamic_cast<ThreeDCubeGame*> (m_pGame);

	m_oMovement.Calculate(iCurrentTime);
	m_iCurrentScreenX = m_oMovement.GetX();
	m_iCurrentScreenY = m_oMovement.GetY();

	if ( m_oMovement.HasMovementFinished( iCurrentTime ) )
	{
		// Object has stopped moving - you probably want to check for key press, and if
		// key is pressed then start a new move.
		if ( ( m_iCurrentMapX != m_iNextMapX ) || (m_iCurrentMapY != m_iNextMapY ) )
		{
			switch( pGame->GetMapEntry( m_iNextMapX, m_iNextMapY ) )
			{
				//change colour of the block when the ball is in mid motion towards it
			case 'B': 
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'A' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'A' );
				pGame->RecordColourChanged();
				//if powered up then add the score by ten
				if(pGame->m_iPowerTime < 10000)
					pGame->m_iScore=pGame->m_iScore+10;
				else
					pGame->m_iScore++;
				break;
			case 'C':  
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'B' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'B' );
				pGame->RecordColourChanged(); 
				//if powered up then add the score by ten
				if(pGame->m_iPowerTime < 10000)
					pGame->m_iScore=pGame->m_iScore+10;
				else
					pGame->m_iScore++;
				break;
			case 'D': 
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'C' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'C' );
				pGame->RecordColourChanged();
				//if powered up then add the score by ten
				if(pGame->m_iPowerTime < 10000)
					pGame->m_iScore=pGame->m_iScore+10;
				else
					pGame->m_iScore++;
				break;
			case 'E': 
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'D' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'D' );
				pGame->RecordColourChanged();
				//if powered up then add the score by ten
				if(pGame->m_iPowerTime < 10000)
					pGame->m_iScore=pGame->m_iScore+10;
				else
					pGame->m_iScore++;
				break;
			case 'F': 
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'E' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'E' );
				pGame->RecordColourChanged();
				//if powered up then add the score by ten
				if(pGame->m_iPowerTime < 10000)
					pGame->m_iScore=pGame->m_iScore+10;
				else
					pGame->m_iScore++;
				break;
			case 'H':
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'A' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'A' );
				//activate hyperspeed
				pGame->m_iHyperSpeed=true;
				break;
			case 'P':
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'A' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'A' );
				//activate power for bonus points
				pGame->m_iPowerStatus=true;
				break;
			case 'L':
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'A' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'A' );
				//add a life if player steps on L
				pGame->m_iLivesLeft++;
				break;
			case 'I':
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'A' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'A' );
				//activate immunity
				pGame->m_iImmunityStatus=true;
				break;
			case 'S'://opponents cannot enter this safe block
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'S' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'S' );
				break;
			case 'M':
				pGame->SetMapEntry( m_iNextMapX, m_iNextMapY, 'A' ); 
				pGame->DrawCube( pGame->GetScreenXForMapX( m_iNextMapX ), pGame->GetScreenYForMapY( m_iNextMapY ), true, 'A' );
				//activate opponents freeze
				pGame->m_iFreezeStatus=true;
				break;
			}
		}		
			m_iCurrentMapX = m_iNextMapX;
			m_iCurrentMapY = m_iNextMapY;

		if ( m_pGame->IsKeyPressed( SDLK_UP ) //upward movement
			&& (m_iCurrentMapY > 0 )
			&& CanEnter(m_iCurrentMapX-1,m_iCurrentMapY-1) ) //can player enter to where its moving?
		{
			int iCurrentTime= m_pGame-> GetTime();
			m_iCurrentMoveDirection = 1;
			m_iNextMapY = m_iCurrentMapY - 1;
			m_iNextMapX = m_iCurrentMapX - 1;
			//altered so that ball moves slower uphill
			SetMovementPosition( 500 );
		}
		else if ( m_pGame->IsKeyPressed( SDLK_DOWN ) //downward movement 
			&& (m_iCurrentMapY < (ThreeDCubeGame::MAP_HEIGHT-1) )
			&& CanEnter(m_iCurrentMapX+1,m_iCurrentMapY+1) ) //can player enter to where its moving?
		{			
			int iCurrentTime= m_pGame-> GetTime();
			m_iCurrentMoveDirection = 3;
			m_iNextMapY = m_iCurrentMapY + 1;
			m_iNextMapX = m_iCurrentMapX + 1;
			SetMovementPosition( 300 );
		}
		else if ( m_pGame->IsKeyPressed( SDLK_LEFT ) //leftward movement
			&& (m_iCurrentMapX > 0 )
			&& CanEnter(m_iCurrentMapX-1,m_iCurrentMapY+1) ) //can player enter to where its moving?
		{			
			int iCurrentTime= m_pGame-> GetTime();
			m_iCurrentMoveDirection = 0;
			m_iNextMapX = m_iCurrentMapX - 1;			
			m_iNextMapY = m_iCurrentMapY + 1;
			SetMovementPosition( 300 );
		}
		else if ( m_pGame->IsKeyPressed( SDLK_RIGHT ) //rightward movement
			&& (m_iCurrentMapX < (ThreeDCubeGame::MAP_WIDTH-1) )
			&& CanEnter(m_iCurrentMapX+1,m_iCurrentMapY-1) ) //can player enter to where its moving?
		{			
			int iCurrentTime= m_pGame-> GetTime();
			m_iCurrentMoveDirection = 2;
			m_iNextMapX = m_iCurrentMapX + 1;			
			m_iNextMapY = m_iCurrentMapY - 1;
			//altered so that ball moves slower uphill
			SetMovementPosition( 500 );
		}
	}
	// Ensure that the object gets redrawn on the display, if something changed
	RedrawObjects();
}

bool ThreeDCubePlayer::CanEnter( int iMapX, int iMapY )
{
	//dynamic cast for safety
	ThreeDCubeGame* pGame = dynamic_cast<ThreeDCubeGame*>(m_pGame);

	switch( pGame->GetMapEntry( iMapX, iMapY ) )
	{
	//player can enter all tiles except for X
	case 'A': return true;break;
	case 'B': return true;break;
	case 'C': return true;break;
	case 'D': return true;break;
	case 'E': return true;break;
	case 'F': return true;break;
	case 'H': return true;break;
	case 'P': return true;break;
	case 'L': return true;break;
	case 'I': return true;break;
	case 'S': return true;break;
	case 'M': return true;break;
	case 'X': return false;break;
	}
	return false;
}


/** Assumes that the current and next map coordinates have been set, and uses 
them to work out the starting and ending screen positions for the move.
Has an optional mid-point for the move, which allows the move to avoid being a straight line.
(i.e. makes it two straight lines - like a kind of jumping movement */
void ThreeDCubePlayer::SetMovementPosition( int iMoveDuration )
{
	ThreeDCubeGame* pGame = dynamic_cast <ThreeDCubeGame*>(m_pGame);
	int iCurrentTime = m_pGame->GetTime();

	int iX1 = ThreeDCubeGame::MAP_X_OFFSET + ThreeDCubeGame::BLOCK_SIZE * m_iCurrentMapX + POSITION_OFFSET_X;
	//makes sure that ball bounces to the correct block by multiplying by 2 for y
	int iY1 = ThreeDCubeGame::MAP_Y_OFFSET + ThreeDCubeGame::BLOCK_SIZE * m_iCurrentMapY * 2+ POSITION_OFFSET_Y;
	int iX3 = ThreeDCubeGame::MAP_X_OFFSET + ThreeDCubeGame::BLOCK_SIZE * m_iNextMapX + POSITION_OFFSET_X;
	//makes sure that ball bounces to the correct block by multiplying by 2 for y
	int iY3 = ThreeDCubeGame::MAP_Y_OFFSET + ThreeDCubeGame::BLOCK_SIZE * m_iNextMapY *2 + POSITION_OFFSET_Y;
	// This movement position thing allows a different mid-point.
	// For this example, we'll make the mid-point the average of the other two.
	
	int iX2 = (iX1 + iX3 )/2; 
	//gives a bounce
	int iY2 = (iY1 + iY3)/2-40;

	m_oMovement.Setup(	iX1, iY1, iX2, iY2, iX3, iY3, iCurrentTime, iCurrentTime+iMoveDuration );
	// Calculate the new position
	m_oMovement.Calculate(iCurrentTime);
	m_iCurrentScreenX = m_oMovement.GetX();
	m_iCurrentScreenY = m_oMovement.GetY();
}
